# coding=utf-8
import os
from string import Template

from typing import List
from parse.sdb_parser import ECU
from utils.templete_file import TempleteFile
from utils.util import writeFile


templete_h = Template(
    """
#pragma once

#include <vector>
#include <transfer/common.h>
#include <arpa/inet.h>
#include "common.h"

namespace ecarx::transfer::utils{
    
    ipcp::IpcpValue toIpcpValue(const Signal &);

}
"""
)
templete_cpp = Template(
    """
#include "convertToIPCPValue.h"
#include "vp/SignalServiceId.h"
#include "ap/signal_id.h"
#include <ipcp/ipcp.h>
#include <string.h>

${ecu_h}

namespace ecarx::transfer::utils{


    ipcp::IpcpValue toIpcpValue(const Signal & signal){

        ipcp::IpcpValue ipcp;
        switch(signal.signalId){
${code_cases}
        }
        return ipcp;
    }
}
"""
)
case_tempelet = Template(
"""
            case static_cast<uint32_t>(${id}):{
${codes}
            }
            break;
""")

base_type_templete = Template("""
                ipcp.service_id=${service_id};
                ipcp.oper_id = ${op_id};
                ipcp.oper_type = IPCP_OPERATION_SETREQUEST_NORETURN;
                ipcp.payload_type = IPCP_DATA_NORMAL;
                memset(ipcp.peer_ip, 0, sizeof(ipcp.peer_ip));
                strcpy(ipcp.peer_ip, SERVICE_IP);
                ipcp.peer_port = SERVICE_PORT;
                ipcp.payload_len = sizeof(Op${signalName}_SetRequestNoReturn);
                Op${signalName}_SetRequestNoReturn* buffer = (Op${signalName}_SetRequestNoReturn*)new uint8_t[ipcp.payload_len];
                ${signals}
                ipcp.payload = (void*)buffer;
                """)

signal_group_templete = Template("""
                ipcp.service_id=${service_id};
                ipcp.oper_id = ${op_id};
                ipcp.oper_type = IPCP_OPERATION_SETREQUEST_NORETURN;
                ipcp.payload_type = IPCP_DATA_NORMAL;
                memset(ipcp.peer_ip, 0, sizeof(ipcp.peer_ip));
                strcpy(ipcp.peer_ip, SERVICE_IP);
                ipcp.peer_port = SERVICE_PORT;
                ipcp.payload_len = sizeof(Op${signalName}_SetRequestNoReturn);
                Op${signalName}_SetRequestNoReturn set;
                memcpy(&set,signal.value.bytes.data(),signal.value.bytes.size());
                Op${signalName}_SetRequestNoReturn* buffer = (Op${signalName}_SetRequestNoReturn*)new uint8_t[ipcp.payload_len];
                ${signals}
                ipcp.payload = (void*)buffer;
                """)
member_templete = Template(
"""
                (*buffer).${member}=${value};""")


class Case(TempleteFile):

    def __init__(self, id) -> None:
        self.id = id
        self.code = []

    def codeBlock(self, code: str):
        self.code = code
        return self

    def toStr(self) -> str:
        return case_tempelet.substitute(id=self.id, codes=self.code)


def generateSignalToIPCPValue(ecus: List[ECU],outDir:str):
    ecu_headers = []
    cases = []
    for ecu in ecus:
        if len(ecu.txSignals) > 0:
            ecu_headers.append(
                "#include \"vp/{0}_TX.h\"".format(ecu.name.upper()))
            # 逐个处理所有的设置信号
            for txSignal in ecu.txSignals:
                case = Case(
                    "SignalTX::{0}".format(txSignal.name))
                members = []
                for signal in txSignal.signals:
                    if txSignal.isSingle():
                        #单一信号
                        value = "signal.value.int32Values[0]"
                        if signal.isFloat():
                            value="signal.value.floatValues[0]"
                        members.append(member_templete.substitute(
                            member="value",
                            value=signal.setSignalToNet(value)
                        ))
                    else:
                        member_name = signal.getVariableName(True,False)
                        members.append(member_templete.substitute(
                            member=member_name,
                            value=signal.setSignalToNet("set."+member_name)))
                if txSignal.isSingle():
                    case.codeBlock(base_type_templete.substitute(
                        service_id="EcarXSignalServiceID::serviceID_{0}".format(
                            ecu.name.upper()),
                        op_id="{0}OpID".format(txSignal.name),
                        signalName=txSignal.name,
                        signals="".join(members)
                    ))
                else:
                    case.codeBlock(signal_group_templete.substitute(
                        service_id="EcarXSignalServiceID::serviceID_{0}".format(
                            ecu.name.upper()),
                        op_id="{0}OpID".format(txSignal.name),
                        signalName=txSignal.name,
                        signals="".join(members)
                    ))
                cases.append(case)

    writeFile(os.path.join(outDir,"src"), "convertToIPCPValue.h",
              lambda file: file.write(templete_h.substitute()))
    writeFile(os.path.join(outDir,"src"), "convertToIPCPValue.cpp", lambda file: file.write(
        templete_cpp.substitute(ecu_h="\n".join(ecu_headers), code_cases="".join([case.toStr() for case in cases]))))
